#!/usr/bin/env python


import fcntl
import os
from pprint import pprint
import shelve
import time


from .utils import makedirs


class datastore:

    def __init__(self):
        self.db = None
        self.DB_FILE = os.getenv('HOME') + '/var/db/tmux-backup.db'
        self.lock = None
        self.LOCK_FILE = self.DB_FILE + '.lock'
        self.LATEST_VERSION = 2

    def __enter__(self):
        self.acquire_lock()
        self.init_db()
        self.upgrade_db_version()
        return self

    def __exit__(self, type, value, traceback):
        self.db.close()
        self.release_lock()

    def acquire_lock(self):
        makedirs(os.path.dirname(os.path.abspath(self.LOCK_FILE)))
        self.lock = open(self.LOCK_FILE, 'w')
        fcntl.flock(self.lock, fcntl.LOCK_EX)

    def release_lock(self):
        fcntl.flock(self.lock, fcntl.LOCK_UN)
        self.lock.close()

    def init_db(self):
        makedirs(os.path.dirname(os.path.abspath(self.DB_FILE)))
        self.db = shelve.open(self.DB_FILE, 'c')
        for x in ('backups', 'shells', 'timers'):
            if x not in self.db:
                self.db[x] = {}

    # DB version management

    def get_db_version(self):
        try:
            return self.db['version']
        except KeyError:
            return 1  # v. 1 had no explicit version stored

    def upgrade_db_version(self):
        current_version = self.get_db_version()
        while current_version < self.LATEST_VERSION:
            if current_version == 1:
                # Move all 'history-*' and 'cwd-*' into 'shells' dict
                shells = {}
                for key in self.db.keys():
                    if key.startswith('history-'):
                        pid = key[len('history-'):]
                        if pid not in shells:
                            shells[pid] = {}
                        shells[pid]['history'] = self.db[key]
                        del self.db[key]
                    elif key.startswith('cwd-'):
                        pid = key[len('cwd-'):]
                        if pid not in shells:
                            shells[pid] = {}
                        shells[pid]['cwd'] = self.db[key]
                        del self.db[key]
                self.db['shells'] = shells
                self.db['version'] = 2

            current_version = self.get_db_version()

    def start_new_backup(self):
        self.db['latest-backup'] = {'sessions': {}}

    def add_session(self, name, size_x, size_y):
        version_data = self.db['latest-backup']
        version_data['sessions'][name] = {
            'size_x': size_x,
            'size_y': size_y,
            'windows': {},
            }
        self.db['latest-backup'] = version_data

    def session_iter(self, version=0):
        for session in self.db[version]['sessions']:
            yield session

    def add_window(self, session, window, name, layout):
        version_data = self.db['latest-backup']
        version_data['sessions'][session]['windows'][window] = {
            'name': name,
            'layout': layout,
            'panes': {},
            }
        self.db['latest-backup'] = version_data

    def window_iter(self, session, version=0):
        for window in self.db[version]['sessions'][session]['windows']:
            yield window

    def add_pane(self, session, window, pane, **properties):
        version_data = self.db['latest-backup']
        version_data['sessions'][session]['windows'][window]['panes'][pane] = properties
        self.db['latest-backup'] = version_data

    def pane_iter(self, session, window, version=0):
        for pane in self.db[version]['sessions'][session]['windows'][window]['panes']:
            yield pane

    def get_session_size_x(self, session, version=0):
        return self.db[version]['sessions'][session]['size_x']

    def get_session_size_y(self, session, version=0):
        return self.db[version]['sessions'][session]['size_y']

    def get_window_name(self, session, window, version=0):
        return self.db[version]['sessions'][session]['windows'][window]['name']

    def get_window_layout(self, session, window, version=0):
        return self.db[version]['sessions'][session]['windows'][window]['layout']

    def get_pane_cwd(self, session, window, pane, version=0):
        return self.db[version]['sessions'][session]['windows'][window]['panes'][pane]['cwd']

    def get_pane_history(self, session, window, pane, version=0):
        return self.db[version]['sessions'][session]['windows'][window]['panes'][pane]['history']

    def get_pane_screenshot(self, session, window, pane, version=0):
        return self.db[version]['sessions'][session]['windows'][window]['panes'][pane]['screenshot']

    def get_most_recent_shell_time(self):
        try:
            return self.db['timers']['latest-shell']
        except KeyError:
            return 0

    def set_most_recent_shell_time(self):
        timers = self.db['timers']
        timers['latest-shell'] = time.time()
        self.db['timers'] = timers

    def get_most_recent_backup_time(self):
        try:
            return self.db['timers']['latest-backup']
        except KeyError:
            return 0

    def set_most_recent_backup_time(self):
        timers = self.db['timers']
        timers['latest-backup'] = time.time()
        self.db['timers'] = timers

    def get_history(self, pid):
        try:
            return self.db['shells'][pid]['history']
        except KeyError:
            return ''

    def set_history(self, pid, history):
        shells = self.db['shells']
        if pid not in shells:
            shells[pid] = {}
        shells[pid]['history'] = history
        self.db['shells'] = shells
        self.set_most_recent_shell_time()

    def get_shell_cwd(self, pid):
        try:
            return self.db['shells'][pid]['cwd']
        except KeyError:
            return ''

    def set_shell_cwd(self, pid, cwd):
        shells = self.db['shells']
        if pid not in shells:
            shells[pid] = {}
        shells[pid]['cwd'] = cwd
        self.db['shells'] = shells
        self.set_most_recent_shell_time()

    def dump(self):
        max_length = 200
        for key in self.db:
            print key, '=>',
            if len(str(self.db[key])) > max_length:
                print str(self.db[key])[:max_length], '... plus', (len(str(self.db[key]))-max_length), 'more'
            else:
                print self.db[key]
        print
        pprint(dict(self.db), depth=100)
